home *** CD-ROM | disk | FTP | other *** search
- #ifndef lintî¬ ile is
- Sid = "$Id: help.c,v 1.8.2.2 1999/10/11 21:24:20 lhecking Exp $";
- #endif
-
- /* GNUPLOT - help.c */
-
- /*[
- * Copyright 1986 - 1993, 1998 Thomas Williams, Colin Kelley
- *
- * Permission to use, copy, and distribute this software and its
- * documentation for any purpose with or without fee is hereby granted,
- * provided that the above copyright notice appear in all copies and
- * that both that copyright notice and this permission notice appear
- * in supporting documentation.
- *
- * Permission to modify the software is granted, but not the right to
- * distribute the complete modified source code. Modifications are to
- * be distributed as patches to the released version. Permission to
- * distribute binaries produced by compiling modified sources is granted,
- * provided you
- * 1. distribute the corresponding source modifications from the
- * released version in the form of a patch file along with the binaries,
- * 2. add special version identification to distinguish your version
- * in addition to the base release version number,
- * 3. provide your name and address as the primary contact for the
- * support of your modified version, and
- * 4. retain our contact information in regard to use of the base
- * software.
- * Permission to distribute the released version of the source code along
- * with corresponding source modifications in the form of a patch file is
- * granted with same provisions 2 through 4 for binary distributions.
- *
- * This software is provided "as is" without express or implied warranty
- * to the extent permitted by applicable law.
- ]*/
-
- #include "plot.h"
-
- #define SAME 0 /* for strcmp() */
-
- #include "help.h" /* values passed back */
-
- void int_error __PROTO((char str[], int t_num));
-
- #if defined(__EMX__) || defined(DJGPP) || defined(DOS386)
- /* we have plenty of memory under __EMX__ or DJGPP */
- # ifdef MSDOS
- # undef MSDOS
- # endif
- #endif
-
- /*
- ** help -- help subsystem that understands defined keywords
- **
- ** Looks for the desired keyword in the help file at runtime, so you
- ** can give extra help or supply local customizations by merely editing
- ** the help file.
- **
- ** The original (single-file) idea and algorithm is by John D. Johnson,
- ** Hewlett-Packard Company. Thanx and a tip of the Hatlo hat!
- **
- ** Much extension by David Kotz for use in gnutex, and then in gnuplot.
- ** Added output paging support, both unix and builtin. Rewrote completely
- ** to read helpfile into memory, avoiding reread of help file. 12/89.
- **
- ** Modified by Russell Lang to avoid reading completely into memory
- ** if MSDOS defined. This uses much less memory. 6/91
- **
- ** The help file looks like this (the question marks are really in column 1):
- **
- ** ?topic
- ** This line is printed when the user wants help on "topic".
- ** ?keyword
- ** ?Keyword
- ** ?KEYWORD
- ** These lines will be printed on the screen if the user wanted
- ** help on "keyword", "Keyword", or "KEYWORD". No casefolding is
- ** done on the keywords.
- ** ?subject
- ** ?alias
- ** This line is printed for help on "subject" and "alias".
- ** ?
- ** ??
- ** Since there is a null keyword for this line, this section
- ** is printed when the user wants general help (when a help
- ** keyword isn't given). A command summary is usually here.
- ** Notice that the null keyword is equivalent to a "?" keyword
- ** here, because of the '?' and '??' topic lines above.
- ** If multiple keywords are given, the first is considered the
- ** 'primary' keyword. This affects a listing of available topics.
- ** ?last-subject
- ** Note that help sections are terminated by the start of the next
- ** '?' entry or by EOF. So you can't have a leading '?' on a line
- ** of any help section. You can re-define the magic character to
- ** recognize in column 1, though, if '?' is too useful. (Try ^A.)
- */
-
- #define KEYFLAG '?' /* leading char in help file topic lines */
-
- /*
- ** Calling sequence:
- ** int result; # 0 == success
- ** char *keyword; # topic to give help on
- ** char *pathname; # path of help file
- ** int subtopics; # set to TRUE if only subtopics to be listed
- ** # returns TRUE if subtopics were found
- ** result = help(keyword, pathname, &subtopics);
- ** Sample:
- ** cmd = "search\n";
- ** helpfile = "/usr/local/lib/program/program.help";
- ** subtopics = FALSE;
- ** if (help(cmd, helpfile, &subtopics) != H_FOUND)
- ** printf("Sorry, no help for %s", cmd);
- **
- **
- ** Speed this up by replacing the stdio calls with open/close/read/write.
- */
- #ifdef WDLEN
- # define PATHSIZE WDLEN
- #else
- # define PATHSIZE BUFSIZ
- #endif
-
- typedef struct line_s LINEBUF;
- struct line_s {
- char *line; /* the text of this line */
- LINEBUF *next; /* the next line */
- };
-
- typedef struct linkey_s LINKEY;
- struct linkey_s {
- char *key; /* the name of this key */
- long pos; /* ftell position */
- LINEBUF *text; /* the text for this key */
- TBOOLEAN primary; /* TRUE -> is a primary name for a text block */
- LINKEY *next; /* the next key in linked list */
- };
-
- typedef struct key_s KEY;
- struct key_s {
- char *key; /* the name of this key */
- long pos; /* ftell position */
- LINEBUF *text; /* the text for this key */
- TBOOLEAN primary; /* TRUE -> is a primary name for a text block */
- };
- static LINKEY *keylist = NULL; /* linked list of keys */
- static KEY *keys = NULL; /* array of keys */
- static int keycount = 0; /* number of keys */
- static FILE *helpfp = NULL;
-
- static int LoadHelp __PROTO((char *path));
- static void sortkeys __PROTO((void));
- static int keycomp __PROTO((struct key_s * a, struct key_s * b));
- static LINEBUF *storeline __PROTO((char *text));
- static LINKEY *storekey __PROTO((char *key));
- static KEY *FindHelp __PROTO((char *keyword));
- static TBOOLEAN Ambiguous __PROTO((struct key_s * key, int len));
-
- /* Help output */
- static void PrintHelp __PROTO((struct key_s * key, int *subtopics));
- static void ShowSubtopics __PROTO((struct key_s * key, int *subtopics));
-
- #if defined(PIPES)
- static FILE *outfile; /* for unix pager, if any */
- #endif
- static int pagelines; /* count for builtin pager */
- #define SCREENSIZE 24 /* lines on screen (most have at least 24) */
-
- /* help:
- * print a help message
- * also print available subtopics, if subtopics is TRUE
- */
- int help(keyword, path, subtopics)
- char *keyword; /* on this topic */
- char *path; /* from this file */
- TBOOLEAN *subtopics; /* (in) - subtopics only? */
- /* (out) - are there subtopics? */
- {
- static char oldpath[PATHSIZE] = ""; /* previous help file */
- int status; /* result of LoadHelp */
- KEY *key; /* key that matches keyword */
-
- /*
- ** Load the help file if necessary (say, first time we enter this routine,
- ** or if the help file changes from the last time we were called).
- ** Also may occur if in-memory copy was freed.
- ** Calling routine may access errno to determine cause of H_ERROR.
- */
- errno = 0;
- if (strncmp(oldpath, path, PATHSIZE) != SAME)
- FreeHelp();
- if (keys == NULL) {
- status = LoadHelp(path);
- if (status == H_ERROR)
- return (status);
-
- /* save the new path in oldpath */
- safe_strncpy(oldpath, path, PATHSIZE);
- }
- /* look for the keyword in the help file */
- key = FindHelp(keyword);
- if (key != NULL) {
- /* found the keyword: print help and return */
- PrintHelp(key, subtopics);
- status = H_FOUND;
- } else {
- status = H_NOTFOUND;
- }
-
- return (status);
- }
-
- /* we only read the file once, into memory
- * except for MSDOS when we don't read all the file -
- * just the keys and location of the text
- */
- static int LoadHelp(path)
- char *path;
- {
- LINKEY *key; /* this key */
- long pos = 0; /* ftell location within help file */
- char buf[BUFSIZ]; /* line from help file */
- LINEBUF *head; /* head of text list */
- LINEBUF *firsthead = NULL;
- TBOOLEAN primary; /* first ? line of a set is primary */
- TBOOLEAN flag;
-
- if ((helpfp = fopen(path, "r")) == NULL) {
- /* can't open help file, so error exit */
- return (H_ERROR);
- }
- /*
- ** The help file is open. Look in there for the keyword.
- */
- if (!fgets(buf, BUFSIZ - 1, helpfp) || *buf != KEYFLAG)
- return (H_ERROR); /* it is probably not the .gih file */
-
- while (!feof(helpfp)) {
- /*
- ** Make an entry for each synonym keyword
- */
- primary = TRUE;
- while (buf[0] == KEYFLAG) {
- key = storekey(buf + 1); /* store this key */
- key->primary = primary;
- key->text = NULL; /* fill in with real value later */
- key->pos = 0; /* fill in with real value later */
- primary = FALSE;
- pos = ftell(helpfp);
- if (fgets(buf, BUFSIZ - 1, helpfp) == (char *) NULL)
- break;
- }
- /*
- ** Now store the text for this entry.
- ** buf already contains the first line of text.
- */
- #ifndef MSDOS
- firsthead = storeline(buf);
- head = firsthead;
- #endif
- while ((fgets(buf, BUFSIZ - 1, helpfp) != (char *) NULL)
- && (buf[0] != KEYFLAG)) {
- #ifndef MSDOS
- /* save text line */
- head->next = storeline(buf);
- head = head->next;
- #endif
- }
- /* make each synonym key point to the same text */
- do {
- key->pos = pos;
- key->text = firsthead;
- flag = key->primary;
- key = key->next;
- } while (flag != TRUE && key != NULL);
- }
- #ifndef MSDOS
- (void) fclose(helpfp);
- #endif
-
- /* we sort the keys so we can use binary search later */
- sortkeys();
- return (H_FOUND); /* ok */
- }
-
- /* make a new line buffer and save this string there */
- static LINEBUF *
- storeline(text)
- char *text;
- {
- LINEBUF *new;
-
- new = (LINEBUF *) malloc(sizeof(LINEBUF));
- if (new == NULL)
- int_error("not enough memory to store help file", -1);
- if (text != NULL) {
- new->line = (char *) malloc((unsigned int) (strlen(text) + 1));
- if (new->line == NULL)
- int_error("not enough memory to store help file", -1);
- (void) strcpy(new->line, text);
- } else
- new->line = NULL;
-
- new->next = NULL;
-
- return (new);
- }
-
- /* Add this keyword to the keys list, with the given text */
- static LINKEY *
- storekey(key)
- char *key;
- {
- LINKEY *new;
-
- key[strlen(key) - 1] = NUL; /* cut off \n */
-
- new = (LINKEY *) malloc(sizeof(LINKEY));
- if (new == NULL)
- int_error("not enough memory to store help file", -1);
- new->key = (char *) malloc((unsigned int) (strlen(key) + 1));
- if (new->key == NULL)
- int_error("not enough memory to store help file", -1);
- (void) strcpy(new->key, key);
-
- /* add to front of list */
- new->next = keylist;
- keylist = new;
- keycount++;
- return (new);
- }
-
- /* we sort the keys so we can use binary search later */
- /* We have a linked list of keys and the number.
- * to sort them we need an array, so we reform them into an array,
- * and then throw away the list.
- */
- static void sortkeys()
- {
- LINKEY *p, *n; /* pointers to linked list */
- int i; /* index into key array */
-
- /* allocate the array */
- keys = (KEY *) malloc((unsigned int) ((keycount + 1) * sizeof(KEY)));
- if (keys == NULL)
- int_error("not enough memory to store help file", -1);
-
- /* copy info from list to array, freeing list */
- for (p = keylist, i = 0; p != NULL; p = n, i++) {
- keys[i].key = p->key;
- keys[i].pos = p->pos;
- keys[i].text = p->text;
- keys[i].primary = p->primary;
- n = p->next;
- free((char *) p);
- }
-
- /* a null entry to terminate subtopic searches */
- keys[keycount].key = NULL;
- keys[keycount].pos = 0;
- keys[keycount].text = NULL;
-
- /* sort the array */
- /* note that it only moves objects of size (two pointers + long + int) */
- /* it moves no strings */
- qsort((char *) keys, keycount, sizeof(KEY), (sortfunc) keycomp);
- }
-
- static int keycomp(a, b)
- KEY *a, *b;
- {
- return (strcmp(a->key, b->key));
- }
-
- /* Free the help file from memory. */
- /* May be called externally if space is needed */
- void FreeHelp()
- {
- int i; /* index into keys[] */
- LINEBUF *t, *next;
-
- if (keys == NULL)
- return;
-
- for (i = 0; i < keycount; i++) {
- free((char *) keys[i].key);
- if (keys[i].primary) /* only try to release text once! */
- for (t = keys[i].text; t != NULL; t = next) {
- free((char *) t->line);
- next = t->next;
- free((char *) t);
- }
- }
- free((char *) keys);
- keys = NULL;
- keycount = 0;
- #ifdef MSDOS
- (void) fclose(helpfp);
- #endif
- }
-
- /* FindHelp:
- * Find the key that matches the keyword.
- * The keys[] array is sorted by key.
- * We could use a binary search, but a linear search will aid our
- * attempt to allow abbreviations. We search for the first thing that
- * matches all the text we're given. If not an exact match, then
- * it is an abbreviated match, and there must be no other abbreviated
- * matches -- for if there are, the abbreviation is ambiguous.
- * We print the ambiguous matches in that case, and return not found.
- */
- static KEY * /* NULL if not found */
- FindHelp(keyword)
- char *keyword; /* string we look for */
- {
- KEY *key;
- int len = strlen(keyword);
- int compare;
-
- for (key = keys, compare = 1; key->key != NULL && compare > 0; key++) {
- compare = strncmp(keyword, key->key, len);
- if (compare == 0) /* we have a match! */
- if (!Ambiguous(key, len)) {
- /* non-ambiguous abbreviation */
- (void) strcpy(keyword, key->key); /* give back the full spelling */
- return (key); /* found!! */
- }
- }
-
- /* not found, or ambiguous */
- return (NULL);
- }
-
- /* Ambiguous:
- * Check the key for ambiguity up to the given length.
- * It is ambiguous if it is not a complete string and there are other
- * keys following it with the same leading substring.
- */
- static TBOOLEAN
- Ambiguous(key, len)
- KEY *key;
- int len;
- {
- char *first;
- char *prev;
- TBOOLEAN status = FALSE; /* assume not ambiguous */
- int compare;
- int sublen;
-
- if (key->key[len] == NUL)
- return (FALSE);
-
- for (prev = first = key->key, compare = 0, key++;
- key->key != NULL && compare == 0; key++) {
- compare = strncmp(first, key->key, len);
- if (compare == 0) {
- /* So this key matches the first one, up to len.
- * But is it different enough from the previous one
- * to bother printing it as a separate choice?
- */
- sublen = instring(prev + len, ' ');
- if (strncmp(key->key, prev, len + sublen) != 0) {
- /* yup, this is different up to the next space */
- if (!status) {
- /* first one we have printed is special */
- fprintf(stderr,
- "Ambiguous request '%.*s'; possible matches:\n",
- len, first);
- fprintf(stderr, "\t%s\n", prev);
- status = TRUE;
- }
- fprintf(stderr, "\t%s\n", key->key);
- prev = key->key;
- }
- }
- }
-
- return (status);
- }
-
- /* PrintHelp:
- * print the text for key
- */
- static void PrintHelp(key, subtopics)
- KEY *key;
- TBOOLEAN *subtopics; /* (in) - subtopics only? */
- /* (out) - are there subtopics? */
- {
- LINEBUF *t;
- #ifdef MSDOS
- char buf[BUFSIZ]; /* line from help file */
- #endif
-
- StartOutput();
-
- if (subtopics == NULL || !*subtopics) {
- #ifdef MSDOS
- fseek(helpfp, key->pos, 0);
- while ((fgets(buf, BUFSIZ - 1, helpfp) != (char *) NULL)
- && (buf[0] != KEYFLAG)) {
- OutLine(buf);
- }
- #else
- for (t = key->text; t != NULL; t = t->next)
- OutLine(t->line); /* print text line */
- #endif
- }
- ShowSubtopics(key, subtopics);
- OutLine("\n");
-
- EndOutput();
- }
-
-
- /* ShowSubtopics:
- * Print a list of subtopic names
- */
- #define PER_LINE 4
-
- static void ShowSubtopics(key, subtopics)
- KEY *key; /* the topic */
- TBOOLEAN *subtopics; /* (out) are there any subtopics */
- {
- int subt = 0; /* printed any subtopics yet? */
- KEY *subkey; /* subtopic key */
- int len; /* length of key name */
- char line[BUFSIZ]; /* subtopic output line */
- char *start; /* position of subname in key name */
- int sublen; /* length of subname */
- int pos = 0;
- int spacelen = 0; /* Moved from inside for() loop */
- char *prev = NULL; /* the last thing we put on the list */
-
- *line = NUL;
- len = strlen(key->key);
-
- for (subkey = key + 1; subkey->key != NULL; subkey++) {
- int ispacelen = 0;
- if (strncmp(subkey->key, key->key, len) == 0) {
- /* find this subtopic name */
- start = subkey->key + len;
- if (len > 0) {
- if (*start == ' ')
- start++; /* skip space */
- else
- break; /* not the same topic after all */
- } else {
- /* here we are looking for main topics */
- if (!subkey->primary)
- continue; /* not a main topic */
- }
- sublen = instring(start, ' ');
- if (prev == NULL || strncmp(start, prev, sublen) != 0) {
- if (subt == 0) {
- subt++;
- if (len)
- (void) sprintf(line, "\nSubtopics available for %s:\n",
- key->key);
- else
- (void) sprintf(line, "\nHelp topics available:\n");
- OutLine(line);
- *line = NUL;
- pos = 0;
- }
- if (pos >= PER_LINE) {
- (void) strcat(line, "\n");
- OutLine(line);
- *line = NUL;
- pos = 0;
- }
- #define FIRSTCOL 4
- #define COLLENGTH 18
-
- /* adapted by DvdSchaaf */
- if (pos == 0)
- spacelen = FIRSTCOL;
- for (ispacelen = 0; ispacelen < spacelen; ispacelen++)
- (void) strcat(line, " ");
- (void) strncat(line, start, sublen);
- spacelen = COLLENGTH - sublen;
-
- while (spacelen <= 0) {
- spacelen += COLLENGTH;
- pos++;
- }
-
- pos++;
- prev = start;
- }
- } else {
- /* new topic */
- break;
- }
- }
-
- /* put out the last line */
- if (subt > 0 && pos > 0) {
- (void) strcat(line, "\n");
- OutLine(line);
- }
- /*
- if (subt == 0) {
- OutLine("\n");
- OutLine("No subtopics available\n");
- }
- */
-
- if (subtopics)
- *subtopics = (subt != 0);
- }
-
-
- /* StartOutput:
- * Open a file pointer to a pipe to user's $PAGER, if there is one,
- * otherwise use our own pager.
- */
- void StartOutput()
- {
- #if defined(PIPES)
- char *pager_name = getenv("PAGER");
-
- if (pager_name != NULL && *pager_name != NUL)
- if ((outfile = popen(pager_name, "w")) != (FILE *) NULL)
- return; /* success */
- outfile = stderr;
- /* fall through to built-in pager */
- #endif
-
- /* built-in pager */
- pagelines = 0;
- }
-
- #if defined(ATARI) || defined(MTOS)
- # ifndef READLINE
- # error cannot compile atari versions without -DREADLINE
- # endif
- #endif
-
- /* write a line of help output */
- /* line should contain only one \n, at the end */
- void OutLine(line)
- char *line;
- {
- int c; /* dummy input char */
- #if defined(PIPES)
- if (outfile != stderr) {
- fputs(line, outfile);
- return;
- }
- #endif
-
- /* built-in dumb pager */
- /* leave room for prompt line */
- if (pagelines >= SCREENSIZE - 2) {
- fputs("Press return for more: ", stderr);
- #if defined(ATARI) || defined(MTOS)
- do
- c = tos_getch();
- while (c != '\x04' && c != '\r' && c != '\n');
- #else
- do
- c = getchar();
- while (c != EOF && c != '\n');
- #endif
- pagelines = 0;
- }
- fputs(line, stderr);
- pagelines++;
- }
-
- void EndOutput()
- {
- #if defined(PIPES)
- if (outfile != stderr)
- (void) pclose(outfile);
- #endif
- }
-